// (C) Andreas Kaiser 2008

#include "config.h"
#include <util/crc16.h>
#include <util/delay.h>
#include "rfm02.h"

#if RFM_CLOCKOUT != 0
  #define DefaultPWR	0
#else
  #define DefaultPWR	(RF02PWR_DisableClockOutput)
#endif

/*
 * Required:
 *	RFM_SOFTWARE	0/1
 *	RFM_BUFFER	buffer size
 *	RFM_BAND	433 / 868
 *	RFM_CLOCKOUT	0 / 1
 *	RFM_CLOCKFREQ	MHz
 *
 *	RFM_SCK_PORT
 *	RFM_SCK_PIN
 *	RFM_MOSI_PORT
 *	RFM_MOSI_PIN
 *	RFM_SS_PORT
 *	RFM_SS_PIN
 *
 *	RFM_FSK_PORT
 *	RFM_FSK_PIN
 *
 *	RFM_IRQ_NAME	INT0/1/2
 *	RFM_IRQ_PORT
 *	RFM_IRQ_PIN
 *	RFM_IRQ_ISC1	bit no
 *	RFM_IRQ_ISC0	bit no
 */

#define SS_INIT()	do{ SS_HI(); setBits(DDR(RFM_SS_PORT), RFM_SS_PIN); }while(0)
#define SS_LO()		clrBits(PORT(RFM_SS_PORT),   RFM_SS_PIN)
#define SS_HI()		setBits(PORT(RFM_SS_PORT),   RFM_SS_PIN)

#define FSK_INIT()	setBits(DDR(RFM_FSK_PORT),  RFM_FSK_PIN)
#define FSK_LO()	clrBits(PORT(RFM_FSK_PORT), RFM_FSK_PIN)
#define FSK_HI()	setBits(PORT(RFM_FSK_PORT), RFM_FSK_PIN)

#define IRQ_INIT()	do{ clrBits(DDR(RFM_IRQ_PORT),  RFM_IRQ_PIN); \
			    setBits(PORT(RFM_IRQ_PORT), RFM_IRQ_PIN); \
			}while(0)
#define IRQ_TEST()	tstBits(PIN(RFM_IRQ_PORT), RFM_IRQ_PIN)

#ifndef EIMSK
  #ifdef GIMSK
    #define EIMSK GIMSK
    #define EIFR  GIFR
    #define EICRA MCUCR
  #else
    #define EIMSK GICR
    #define EIFR  GIFR
    #define EICRA MCUCR
  #endif
#endif    
#define IRQ_ON()	setBits(EIMSK, 1<<RFM_IRQ_NAME)
#define IRQ_OFF()	clrBits(EIMSK, 1<<RFM_IRQ_NAME)
#define IRQ_CLR()	do{ EIFR = 1<<RFM_IRQ_NAME; }while(0)
#define IRQ_SETUP()	do{ EICRA |= 1<<RFM_IRQ_ISC1 | 0<<RFM_IRQ_ISC0;	IRQ_CLR(); }while(0)

//-----------------------------------------------------------------------------

static volatile uint8_t		txdata, txcount;
static volatile uint8_t		done, error;
static uint16_t			crc;

static uint8_t			buffer[RFM_BUFFER];
static uint8_t			appx, irqx;
static volatile uint8_t		count;

//-----------------------------------------------------------------------------
#if RFM_SOFTWARE

static void
spi_init(void)
{
    clrBits(PORT(RFM_SCK_PORT), RFM_SCK_PIN);
    setBits(DDR(RFM_SCK_PORT),  RFM_SCK_PIN);
    setBits(DDR(RFM_MOSI_PORT), RFM_MOSI_PIN);
}

static void
spi_down(void)
{
    clrBits(PORT(RFM_SCK_PORT),  RFM_SCK_PIN);
    clrBits(PORT(RFM_MOSI_PORT), RFM_MOSI_PIN);
}

static void
spi(uint16_t w)
{
    for (uint8_t i = 0; i < 16; ++i) {
	if (w & 0x8000)
	    setBits(PORT(RFM_MOSI_PORT), RFM_MOSI_PIN);
	else
	    clrBits(PORT(RFM_MOSI_PORT), RFM_MOSI_PIN);
	setBits(PORT(RFM_SCK_PORT), RFM_SCK_PIN);
	w <<= 1;
	clrBits(PORT(RFM_SCK_PORT), RFM_SCK_PIN);
    }
}

void
spicmd(uint16_t w)
{
    PRINTF("spicmd:%04X\n", w);
    SS_LO();
    spi(w);
    SS_HI();
}

//-----------------------------------------------------------------------------
#else

#include "spi.h"

void
spicmd(uint16_t w)
{
    PRINTF("spicmd:%04X\n", w);
    spi_mode(0);
    SS_LO();
    spi(w >> 8);
    spi(w);
    SS_HI();
}

#endif
//-----------------------------------------------------------------------------

void
rfm_init(uint16_t frequency, uint16_t clockout)
{
    SS_INIT();
    IRQ_OFF();
    FSK_INIT();
    IRQ_INIT();

#if RFM_BAND== 868
    spicmd(RF02CFG | RF02CFG_Frequency868 | clockout |
	   RF02CFG_CrystalLoad(12.5) | RF02CFG_FrequencyShift(120));
#elif RFM_BAND == 433
    spicmd(RF02CFG | RF02CFG_Frequency433 | clockout |
	   RF02CFG_CrystalLoad(12.5) | RF02CFG_FrequencyShift(90));
#else
    #error RFM_BAND?
#endif
    spicmd(RF02FREQ | frequency);
    spicmd(RF02RATE(4800));
    spicmd(RF02LBTX | RF02LBTX_EnableBitSync | RF02LBTX_Voltage(4.5));
    spicmd(RF02PWR | DefaultPWR);

    IRQ_SETUP();
}

void
rfm_down(void)
{
    spi_down();
    clrBits(PORT(RFM_SS_PORT),  RFM_SS_PIN);		// pins to GND
    clrBits(PORT(RFM_FSK_PORT), RFM_FSK_PIN);
}

void
rfm_cmd(uint16_t w)
{
    spicmd(w);
}

uint8_t
rfm_error(void)
{
    return error;
}

//-----------------------------------------------------------------------------

static void
clear(void)
{
    txcount = 0;
    done  = 0;
    error = 0;
    appx  = 0;
    irqx  = 0;
    count = 0;
    error = 0;
}

//-----------------------------------------------------------------------------

void
rfm_tx_start(void)
{
    clear();
    PRINTF("rfm:start\n");
    rfm_tx(0xAA);
    IRQ_CLR(); IRQ_ON();
    rfm_cmd(RF02PWR | DefaultPWR | RF02PWR_Crystal | RF02PWR_Synthesizer | RF02PWR_Amplifier);
    rfm_tx_wait(0xAA);
    rfm_tx_wait(0xAA);
    rfm_tx_wait(0x2D);
    rfm_tx_wait(0xD4);
    crc = ~0;
}

int
rfm_tx_finish(void)
{
    uint16_t temp = crc;
    rfm_tx_wait(temp);
    rfm_tx_wait(temp >> 8);
    done = 1;
    return rfm_tx_busy();
}

int
rfm_tx_busy(void)
{
    if (error)
	return RFM_UNDERRUN;
    if (count && txcount)
	return RFM_NOTREADY;
    return 0;
}

void
rfm_tx_stop(void)
{
    IRQ_OFF();
    rfm_cmd(RF02PWR | DefaultPWR);
    clear();
    PRINTF("rfm:stopped\n");
}

//-----------------------------------------------------------------------------

int
rfm_tx(uint8_t data)
{
    if (error)
	return RFM_UNDERRUN;
    if (count == RFM_BUFFER)
	return RFM_NOTREADY;

    if (txcount == 0) {
	cli();
	txdata = data;
	txcount = 8;
	sei();
    } else {
	buffer[appx] = data;
	if (++appx == RFM_BUFFER)
	    appx = 0;
	cli();
	++count;
	sei();
    }

    crc = _crc16_update(crc, data);
    return 0;
}

int
rfm_tx_wait(uint8_t data)
{
    while (count == RFM_BUFFER)
	;
    return rfm_tx(data);
}

//-----------------------------------------------------------------------------

ISR(RFM_IRQ_VECTOR)
{
    if (txcount == 0) {
	if (!done)
	    error = 1;
	txdata = 0xAA;
	txcount = 8;
    }

    if (txdata & 0x80)
	FSK_HI();
    else
	FSK_LO();

    txdata <<= 1;
    txcount -= 1;

    if (txcount == 0 && count != 0) {
	txdata = buffer[irqx];
	txcount = 8;
	if (++irqx == RFM_BUFFER)
	    irqx = 0;
	--count;
    }
}

